package com.shailan.projects.webfea;

import com.shailan.util.fem.node.Node;
import com.sun.opengl.util.GLUT;
import demos.util.ObjReader;
import java.awt.*;
import java.awt.event.*;

import java.util.ArrayList;
import java.util.Enumeration;
import javax.media.opengl.*;
import javax.media.opengl.glu.GLU;

/**
 * 
 * @author Bahadir SAYLAN
 *
 */
public class GLRenderer implements GLEventListener, MouseListener, MouseMotionListener, KeyListener, MouseWheelListener {

    private float view_rotx = 0.0f,  view_roty = 0.0f,  view_rotz = 0.0f;
    private float translatex = 0.0f,  translatey = 0.0f,  translatez = 0.0f;
    private float eyex = 15.0f,  eyey = 0.0f,  eyez = 0.0f,  midx = 0.0f,  midy = 0.0f,  midz = 10.5f;
    public float scale = 1.0f;
    private int objGlList;
    private int prevMouseX,  prevMouseY;
    private boolean mouseRButtonDown = false;
    public int mouseDragMode = 0;
    private GLAutoDrawable drawObject;
    private GL gl;
    private GLUT glut = new GLUT();
    private GLU glu = new GLU();
    public boolean toggleWire = false;
    public boolean toggleGrid = false;
    private ObjReader model;
    
    private ModelViewer viewer;

    public void init(GLAutoDrawable drawable) {
        // Use debug pipeline
        // drawable.setGL(new DebugGL(drawable.getGL()));
        this.setDrawObject(drawable);
        this.setGl(drawable.getGL());
        

        System.err.println("INIT GL IS: " + gl.getClass().getName());

        System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities());

        gl.setSwapInterval(1);

        //gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, pos, 0);
        gl.glEnable(GL.GL_CULL_FACE);
        //gl.glEnable(GL.GL_LIGHTING); Lightning Disabled
        gl.glEnable(GL.GL_LIGHT0);
        gl.glEnable(GL.GL_DEPTH_TEST);

        //loadModel();

        gl.glEnable(GL.GL_NORMALIZE);

        //Add mouse,key,mouse wheel listeners
        drawable.addMouseListener(this);
        drawable.addMouseMotionListener(this);
        drawable.addKeyListener(this);
        drawable.addMouseWheelListener(this);

    }

    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        
        float h = (float) height / (float) width;

        gl.glMatrixMode(GL.GL_PROJECTION);

        System.err.println("GL_VENDOR: " + gl.glGetString(GL.GL_VENDOR));
        System.err.println("GL_RENDERER: " + gl.glGetString(GL.GL_RENDERER));
        System.err.println("GL_VERSION: " + gl.glGetString(GL.GL_VERSION));
        gl.glLoadIdentity();
        //glu.gluPerspective(45.0f, 2.0f, 10.0f, -5.0f);
        //gl.glOrtho(-100.0, 100.0, -100, 100, -50.0, 50.0);
        gl.glFrustum(-1.0f, 1.0f, -h, h, 5.0f, 120.0f);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
        gl.glTranslatef(0.0f, 0.0f, -40.0f);
    }

    public void display(GLAutoDrawable drawable) {
        // Get the GL corresponding to the drawable we are animating
        
        // Special handling for the case where the GLJPanel is translucent
        // and wants to be composited with other Java 2D content
        if ((drawable instanceof GLJPanel) &&
                !((GLJPanel) drawable).isOpaque() &&
                ((GLJPanel) drawable).shouldPreserveColorBufferIfTranslucent()) {
            gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
        } else {
            gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
            gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        }

        
        gl.glPushMatrix();
        gl.glRotatef(view_rotx, 1.0f, 0.0f, 0.0f);
        gl.glRotatef(view_roty, 0.0f, 1.0f, 0.0f);
        gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f);
        gl.glTranslatef(translatex, translatey, translatez);
        gl.glScalef(scale, scale, scale);

        if (toggleWire) {
            gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);
        }
        gl.glColor3f(0.0f, 1.0f, 0.0f);
        viewer.draw(gl);
        drawAxes();
        
        // Remember that every push needs a pop; this one is paired with
        
        gl.glPopMatrix();


    }

    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
    }

    public void drawAxes() {
        gl.glPushMatrix();
        gl.glRotatef(view_rotx, 0.0f, 0.0f, 0.0f);
        gl.glRotatef(view_roty, 0.0f, 0.0f, 0.0f);
        gl.glLineWidth(2.0f);

        gl.glBegin(gl.GL_LINES);
            gl.glColor3f(1.0f, 0.0f, 0.0f); // X axis is red.
            gl.glVertex3f(0.0f, 0.0f, 0.0f);
            gl.glVertex3f(0.5f, 0.0f, 0.0f);
            gl.glColor3f(0.0f, 1.0f, 0.0f); // Y axis is green.
            gl.glVertex3f(0.0f, 0.0f, 0.0f);
            gl.glVertex3f(0.0f, 0.5f, 0.0f);
            gl.glColor3f(0.0f, 0.0f, 1.0f); // z axis is blue.
            gl.glVertex3f(0.0f, 0.0f, 0.0f);
            gl.glVertex3f(0.0f, 0.0f, 0.5f);
        gl.glEnd();

        gl.glPopMatrix();
    }


    // Methods required for the implementation of MouseListener
    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseWheelMoved(MouseWheelEvent e) {
        int notches = e.getWheelRotation();
        if (scale - notches * 0.2f > 0) {
            //Mouse Wheel Moved UP
            scale = scale - notches * 0.2f;
        }

    }

    public void mousePressed(MouseEvent e) {
        prevMouseX = e.getX();
        prevMouseY = e.getY();
        if ((e.getModifiers() & e.BUTTON3_MASK) != 0) {
            mouseRButtonDown = true;
        }


    }

    public void mouseReleased(MouseEvent e) {
        if ((e.getModifiers() & e.BUTTON3_MASK) != 0) {
            mouseRButtonDown = false;
        }
    }

    public void mouseClicked(MouseEvent e) {
    }

    // Methods required for the implementation of MouseMotionListener
    public void mouseDragged(MouseEvent e) {
        int x = e.getX();
        int y = e.getY();
        Dimension size = e.getComponent().getSize();

        float cx = ((float) (x - prevMouseX) / (float) size.width);
        float cy = ((float) (prevMouseY - y) / (float) size.height);

        switch (mouseDragMode) {
            case 0:
                //Rotate Mode
                float thetaY = 360.0f * cx;
                float thetaX = 360.0f * cy;
                view_roty += thetaY * 0.1;
                view_rotx += thetaX * 0.1;
                
                break;
            case 1:
                //Move Mode
                translatex += 2 * cx;
                translatey += 2 * cy;
                
                break;
            case 2:
                //Scale Mode
                scale += cx;
                
                break;

            default:
                break;
        }
        prevMouseX = x;
        prevMouseY = y;

    }

    public void mouseMoved(MouseEvent e) {
    }
    private boolean[] b = new boolean[256];

    private void dispatchKey(char k) {
        setFlag(k, !getFlag(k));

        switch (k) {
            case 'w':
                eyex++;
                
                break;
            case 's':
                eyex--;
                
                break;
            case 'd':
                eyey++;
                
                break;
            case 'a':
                eyey--;
                
                break;
            default:
                break;
        }
    }

    private void setFlag(char key, boolean val) {
        b[((int) key) & 0xFF] = val;
    }

    private boolean getFlag(char key) {
        return b[((int) key) & 0xFF];
    }

    /**
     * @return the drawObject
     */
    public GLAutoDrawable getDrawObject() {
        return drawObject;
    }

    /**
     * @param drawObject the drawObject to set
     */
    public void setDrawObject(GLAutoDrawable drawObject) {
        this.drawObject = drawObject;
    }

    public void keyReleased(KeyEvent e) {
    }

    public void keyPressed(KeyEvent e) {
    }

    public void keyTyped(KeyEvent e) {
        dispatchKey(e.getKeyChar());
    }

    /**
     * @return the gl
     */
    public GL getGl() {
        return gl;
    }

    /**
     * @param gl the gl to set
     */
    public void setGl(GL gl) {
        this.gl = gl;
    }

    /**
     * @return the viewer
     */
    public ModelViewer getViewer() {
        return viewer;
    }

    /**
     * @param viewer the viewer to set
     */
    public void setViewer(ModelViewer viewer) {
        this.viewer = viewer;
    }

    /**
     * Sets the view
     */
    public void setView(float view_rotx, float view_roty, float view_rotz) {
        this.view_rotx = view_rotx;
        this.view_roty = view_roty;
        this.view_rotz = view_rotz;
    }

    public void printViewStatus() {
        System.out.println("eyeX:" + eyex + "eyeY" + eyey + "\nscale:" + scale + "\nviewRotX:" + view_rotx + "viewRotY:" + view_roty);
    }
}

